// cSpell:ignore bson
package checks

import (
	"context"
	"fmt"
	"log"
	"time"

	"mongoscan/config"

	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

type ServerStatus struct {
	// 服务器基础状态
	Uptime            int64  `bson:"uptime"`
	Host              string `bson:"host"`
	Version           string
	Process           string
	PID               int64    `bson:"pid"`
	Alive             bool     `bson:"ok"`
	AdvisoryHostFQDNs []string `bson:"advisoryHostFQDNs,omitempty"`
	OsArch            string
	Port              int64

	DatabaseStats      []DatabaseStat `bson:"databaseStats"`
	TotalDatabaseStats DatabaseStat   `bson:"totalDatabaseStats"`
	// 慢查询相关
	SlowQueryStats struct {
		Last24H int `bson:"last24h"`
		Last7D  int `bson:"last7d"`
		Last30D int `bson:"last30d"`
		Total   int `bson:"total"`
	} `bson:"slowQueryStats"`

	// 连接与客户端
	Connections struct {
		Current      int `bson:"current"`
		Available    int `bson:"available"`
		TotalCreated int `bson:"totalCreated"`
		Active       int `bson:"active"`
	} `bson:"connections"`

	GlobalLock struct {
		ActiveClients struct {
			Total   int `bson:"total"`
			Readers int `bson:"readers"`
			Writers int `bson:"writers"`
		} `bson:"activeClients"`
	} `bson:"globalLock"`

	// 内存与资源使用
	Mem struct {
		Resident int `bson:"resident"`
		Virtual  int `bson:"virtual"`
	} `bson:"mem"`

	ExtraInfo struct {
		HeapUsageBytes         int `bson:"heap_usage_bytes"`
		MaxResidentSetKB       int `bson:"maxResidentSetKilobytes"`
		PageFaults             int `bson:"page_faults"`
		PageReclaims           int `bson:"page_reclaims"`
		UserTimeMicroseconds   int `bson:"user_time_usec"`
		SystemTimeMicroseconds int `bson:"system_time_usec"`
	} `bson:"extra_info"`

	// 锁与队列
	GlobalLockCurrentQueue struct {
		Total   int `bson:"total"`
		Readers int `bson:"readers"`
		Writers int `bson:"writers"`
	} `bson:"globalLock.currentQueue"`

	// 操作计数器
	Opcounters struct {
		Insert  int `bson:"insert"`
		Query   int `bson:"query"`
		Update  int `bson:"update"`
		Delete  int `bson:"delete"`
		GetMore int `bson:"getmore"`
		Command int `bson:"command"`
	} `bson:"opcounters"`

	OpcountersRepl struct {
		Insert  int `bson:"insert"`
		Query   int `bson:"query"`
		Update  int `bson:"update"`
		Delete  int `bson:"delete"`
		GetMore int `bson:"getmore"`
		Command int `bson:"command"`
	} `bson:"opcountersRepl"`

	// 存储引擎
	WiredTiger struct {
		Cache struct {
			MaxBytesConfigured     int `bson:"maximum bytes configured"`
			BytesInCache           int `bson:"bytes currently in the cache"`
			DirtyBytes             int `bson:"tracked dirty bytes in the cache"`
			UnmodifiedPagesEvicted int `bson:"unmodified pages evicted"`
			PagesReadIntoCache     int `bson:"pages read into cache"`
			PagesWrittenFromCache  int `bson:"pages written from cache"`
		} `bson:"cache"`
	} `bson:"wiredTiger"`

	// 副本集状态
	Repl struct {
		IsMaster  bool     `bson:"ismaster"`
		Secondary bool     `bson:"secondary"`
		Hosts     []string `bson:"hosts"`
		Oplog     struct {
			TimeDiff int64 `bson:"timeDiff"`
		} `bson:"oplog"`
	} `bson:"repl"`

	Metrics struct {
		Repl struct {
			Buffer struct {
				Count     int `bson:"count"`
				SizeBytes int `bson:"sizeBytes"`
			} `bson:"buffer"`
			Apply struct {
				Batches struct {
					Num         int `bson:"num"`
					TotalMillis int `bson:"totalMillis"`
				} `bson:"batches"`
			} `bson:"apply"`
			Network struct {
				Bytes    int `bson:"bytes"`
				Getmores int `bson:"getmores_num"`
				Ops      int `bson:"ops"`
			} `bson:"network"`
		} `bson:"repl"`
	} `bson:"metrics"`

	// 网络与I/O
	Network struct {
		BytesIn  int `bson:"bytesIn"`
		BytesOut int `bson:"bytesOut"`
	} `bson:"network"`

	// 断言
	Asserts struct {
		User    int `bson:"user"`
		Warning int `bson:"warning"`
	} `bson:"asserts"`

	// 锁相关
	LockStats struct {
		GlobalLockTotalTime  int64   `bson:"globalLockTotalTime"`
		CurrentQueueReaders  int     `bson:"currentQueueReaders"`
		CurrentQueueWriters  int     `bson:"currentQueueWriters"`
		GlobalWriteLockPct   float64 `bson:"globalWriteLockPct"`
		DatabaseWriteLockPct float64 `bson:"databaseWriteLockPct"`
	} `bson:"lockStats"`
}

var status ServerStatus

func CollectStatus(cfg *config.Config) (*ServerStatus, error) {
	// 构建连接字符串
	var uri string
	if cfg.User != "" && cfg.Password != "" {
		uri = fmt.Sprintf("mongodb://%s:%s@%s:%d", cfg.User, cfg.Password, cfg.Host, cfg.Port)
	} else {
		uri = fmt.Sprintf("mongodb://%s:%d", cfg.Host, cfg.Port)
	}

	// 设置连接超时
	ctx, cancel := context.WithTimeout(context.Background(), cfg.Timeout)
	defer cancel()

	// 连接MongoDB
	clientOptions := options.Client().ApplyURI(uri)
	client, err := mongo.Connect(ctx, clientOptions)
	if err != nil {
		return nil, fmt.Errorf("连接失败: %v", err)
	}
	defer client.Disconnect(ctx)

	// 获取服务器状态
	err = client.Database("admin").RunCommand(ctx, bson.D{{Key: "serverStatus", Value: 1}}).Decode(&status)
	if err != nil {
		return nil, fmt.Errorf("获取服务器状态失败: %v", err)
	}

	err = baseInfo(ctx, client)
	if err != nil {
		return nil, err
	}

	err = replSetInfo(ctx, client)
	if err != nil {
		return nil, err
	}

	err = databaseStatsInfo(ctx, client)
	if err != nil {
		return nil, err
	}

	err = collectSlowQueryStats(ctx, client)
	if err != nil {
		return nil, err
	}

	err = CollectLockStats(ctx, client, &status)
	if err != nil {
		return nil, err
	}

	return &status, nil
}

// 基础信息
func baseInfo(ctx context.Context, client *mongo.Client) error {
	// 获取构建信息以获取完整版本
	var buildInfo struct {
		Version          string
		Host             string
		Process          string
		PID              int64
		BuildEnvironment struct {
			TargetOS   string `bson:"target_os"`
			TargetArch string `bson:"target_arch"`
		} `bson:"buildEnvironment"`
	}

	err := client.Database("admin").RunCommand(ctx, bson.D{{Key: "buildInfo", Value: 1}}).Decode(&buildInfo)
	if err != nil {
		return err
	}
	if err == nil {
		status.Version = buildInfo.Version
		status.Process = buildInfo.Process
		status.OsArch = buildInfo.BuildEnvironment.TargetOS + " " + buildInfo.BuildEnvironment.TargetArch
	}

	type cmdLineOpts struct {
		Parsed struct {
			Net struct {
				Port int64 `bson:"port"`
			} `bson:"net"`
		} `bson:"parsed"`
	}

	var opts cmdLineOpts
	err = client.Database("admin").RunCommand(ctx, bson.D{{Key: "getCmdLineOpts", Value: 1}}).Decode(&opts)
	if err != nil {
		log.Printf("警告: 获取端口失败: %v", err)
	}
	if err == nil {
		status.Port = opts.Parsed.Net.Port
	}

	return nil
}

// 复制集信息
func replSetInfo(ctx context.Context, client *mongo.Client) error {
	// 获取副本集状态
	if status.Repl.IsMaster || status.Repl.Secondary {
		var rsStatus struct {
			Members []struct {
				Name string
				Lag  int64
			}
		}
		err := client.Database("admin").RunCommand(ctx, bson.D{{Key: "replSetGetStatus", Value: 1}}).Decode(&rsStatus)
		if err == nil && len(rsStatus.Members) > 0 {
			// 计算最大复制延迟
			maxLag := int64(0)
			for _, member := range rsStatus.Members {
				if member.Lag > maxLag {
					maxLag = member.Lag
				}
			}
			status.Repl.Oplog.TimeDiff = maxLag
		}
	}
	return nil
}

type DatabaseStat struct {
	Name        string  `bson:"-"`
	Collections int64   `bson:"collections"`
	Objects     int64   `bson:"objects"`
	AvgObjSize  float64 `bson:"avgObjSize"`
	DataSize    float64 `bson:"dataSize"`
	StorageSize float64 `bson:"storageSize"`
	Indexes     int64   `bson:"indexes"`
	IndexSize   float64 `bson:"indexSize"`
}

func databaseStatsInfo(ctx context.Context, client *mongo.Client) error {
	// 获取数据库列表
	dbList, err := client.ListDatabaseNames(ctx, bson.M{})
	if err != nil {
		return fmt.Errorf("获取数据库列表失败: %v", err)
	}

	// 初始化
	status.DatabaseStats = make([]DatabaseStat, 0)

	// 过滤系统数据库
	systemDBs := map[string]bool{
		"admin":  false,
		"local":  false,
		"config": false,
	}

	// 收集统计信息
	totalStats := DatabaseStat{} // 用于计算总计

	for _, dbName := range dbList {
		if systemDBs[dbName] {
			continue
		}

		// 获取单个数据库统计
		var stats DatabaseStat
		err := client.Database(dbName).RunCommand(ctx, bson.D{
			{Key: "dbStats", Value: 1},
		}).Decode(&stats)
		if err != nil {
			log.Printf("警告: 获取数据库 %s 统计信息失败: %v", dbName, err)
			continue
		}

		// 设置数据库名称
		stats.Name = dbName

		// 添加到列表
		status.DatabaseStats = append(status.DatabaseStats, stats)

		// 计算总计
		totalStats.Objects += stats.Objects
		totalStats.DataSize += stats.DataSize
		totalStats.StorageSize += stats.StorageSize
		totalStats.Indexes += stats.Indexes
		totalStats.IndexSize += stats.IndexSize
	}

	// 存储总计
	status.TotalDatabaseStats = totalStats

	return nil
}

func collectSlowQueryStats(ctx context.Context, client *mongo.Client) error {
	// 获取所有数据库列表（排除系统数据库）
	dbList, err := client.ListDatabaseNames(ctx, bson.M{})
	if err != nil {
		return fmt.Errorf("获取数据库列表失败: %v", err)
	}

	// 设置时间范围
	now := time.Now()
	timeRanges := map[string]time.Time{
		"last24h": now.Add(-24 * time.Hour),
		"last7d":  now.Add(-7 * 24 * time.Hour),
		"last30d": now.Add(-30 * 24 * time.Hour),
	}

	// 初始化计数器
	counts := make(map[string]int)

	for _, dbName := range dbList {
		// 跳过系统数据库
		if dbName == "admin" || dbName == "local" || dbName == "config" {
			continue
		}

		// 获取数据库的system.profile集合
		db := client.Database(dbName)
		collections, err := db.ListCollectionNames(ctx, bson.M{})
		if err != nil {
			log.Printf("警告: 获取数据库 %s 集合列表失败: %v", dbName, err)
			continue
		}

		// 检查是否有system.profile集合
		hasProfile := false
		for _, coll := range collections {
			if coll == "system.profile" {
				hasProfile = true
				break
			}
		}

		if !hasProfile {
			continue
		}

		// 查询不同时间范围的慢查询
		for rangeName, startTime := range timeRanges {
			count, err := db.Collection("system.profile").CountDocuments(ctx, bson.M{
				"ts":     bson.M{"$gte": startTime},
				"millis": bson.M{"$gte": 100}, // 假设慢查询阈值是100毫秒
			})
			if err != nil {
				log.Printf("警告: 查询数据库 %s 的慢查询统计失败: %v", dbName, err)
				continue
			}
			counts[rangeName] += int(count)
		}

		// 查询总慢查询数
		totalCount, err := db.Collection("system.profile").CountDocuments(ctx, bson.M{
			"millis": bson.M{"$gte": 100}, // 假设慢查询阈值是100毫秒
		})
		if err != nil {
			log.Printf("警告: 查询数据库 %s 的总慢查询数失败: %v", dbName, err)
			continue
		}
		counts["total"] += int(totalCount)
	}

	// 将结果存入status结构
	status.SlowQueryStats.Last24H = counts["last24h"]
	status.SlowQueryStats.Last7D = counts["last7d"]
	status.SlowQueryStats.Last30D = counts["last30d"]
	status.SlowQueryStats.Total = counts["total"]

	return nil
}

// CollectLockStats 收集锁状态数据并写入 ServerStatus
func CollectLockStats(ctx context.Context, client *mongo.Client, status *ServerStatus) error {
	// 1. 获取全局锁和队列状态
	if err := getGlobalLockStatus(ctx, client, status); err != nil {
		return fmt.Errorf("获取全局锁状态失败: %v", err)
	}

	// 2. 获取锁模式占比
	if err := getLockModePercentages(ctx, client, status); err != nil {
		return fmt.Errorf("获取锁模式占比失败: %v", err)
	}

	return nil
}

// getGlobalLockStatus 获取全局锁等待时间和当前队列
func getGlobalLockStatus(ctx context.Context, client *mongo.Client, status *ServerStatus) error {
	var result struct {
		GlobalLock struct {
			TotalTime    int64 `bson:"totalTime"` // 微秒
			CurrentQueue struct {
				Readers int `bson:"readers"`
				Writers int `bson:"writers"`
			} `bson:"currentQueue"`
		} `bson:"globalLock"`
	}

	err := client.Database("admin").RunCommand(ctx, bson.D{
		{Key: "serverStatus", Value: 1},
	}).Decode(&result)
	if err != nil {
		return err
	}

	// 写入到 ServerStatus
	status.LockStats.GlobalLockTotalTime = result.GlobalLock.TotalTime
	status.LockStats.CurrentQueueReaders = result.GlobalLock.CurrentQueue.Readers
	status.LockStats.CurrentQueueWriters = result.GlobalLock.CurrentQueue.Writers

	return nil
}

// getLockModePercentages 计算锁模式占比
func getLockModePercentages(ctx context.Context, client *mongo.Client, status *ServerStatus) error {
	var locksData struct {
		Locks struct {
			Global struct {
				AcquireCount struct {
					R int64 `bson:"r"`
					W int64 `bson:"w"`
				} `bson:"acquireCount"`
			} `bson:"Global"`
			Database struct {
				AcquireCount struct {
					R int64 `bson:"r"`
					W int64 `bson:"w"`
				} `bson:"acquireCount"`
			} `bson:"Database"`
		} `bson:"locks"`
	}

	err := client.Database("admin").RunCommand(ctx, bson.D{
		{Key: "serverStatus", Value: 1},
		{Key: "locks", Value: 1},
	}).Decode(&locksData)
	if err != nil {
		return err
	}

	// 计算总锁计数
	totalGlobal := locksData.Locks.Global.AcquireCount.R + locksData.Locks.Global.AcquireCount.W
	totalDatabase := locksData.Locks.Database.AcquireCount.R + locksData.Locks.Database.AcquireCount.W
	totalAll := totalGlobal + totalDatabase

	// 计算百分比并写入 ServerStatus
	if totalAll > 0 {
		status.LockStats.GlobalWriteLockPct = float64(locksData.Locks.Global.AcquireCount.W) / float64(totalAll) * 100
		status.LockStats.DatabaseWriteLockPct = float64(locksData.Locks.Database.AcquireCount.W) / float64(totalAll) * 100
	}

	return nil
}
